﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;

namespace Dotnet.Utils.Utility.AssemblyUtil
{
    /*
     AttributeTargets.All 表示特性可以用于任何元素
     AttributeTargets.Class 表示特性可以用于类
     AttributeTargets.Method 表示特性可以用于方法
     AttributeTargets.Property 表示特性可以用于属性
     AttributeTargets.Field 字段

    AllowMultiple：指定特性是否允许多次使用。如果设置为 true，则特性可以在同一元素上使用多次；如果设置为 false，则特性只能在同一元素上使用一次。默认值为 false。

    Inherited：指定特性是否可以被继承。如果设置为 true，则特性可以被派生类继承；如果设置为 false，则特性不能被派生类继承。默认值为 true。
     */

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public class MyAttribute : Attribute
    {
    }

    public static class AttributeUtil
    {
        public static readonly BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase;

        /// <summary>
        /// 获取包含该特性的所有类
        /// </summary>
        /// <param name="type"></param>
        /// <param name="inherit">true:包含父类,false：不包含父类</param>
        /// <returns></returns>
        public static IEnumerable<Type> GetTypesWithAttribute(IEnumerable<Type> types, Type attributeType, bool inherit = true)
        {
            return types.Where(t => t.IsDefined(attributeType, inherit));
        }

        /// <summary>
        /// 获取类/方法/属性/字段所有特性
        /// </summary>
        /// <param name="type"></param>
        /// <param name="inherit">true:包含父类,false：不包含父类</param>
        /// <returns></returns>
        public static IEnumerable<Type> GetAttributeTypes(MemberInfo memberInfo, bool inherit = true)
        {
            return memberInfo.GetCustomAttributes(inherit).Select(a => a.GetType());
        }

        /// <summary>
        /// 获取特性的所有值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="memberInfo"></param>
        /// <param name="inherit">true:包含父类,false：不包含父类</param>
        /// <returns></returns>
        public static Dictionary<string, object> GetAttributeProperties(this MemberInfo memberInfo, Type attributeType, bool inherit = true)
        {
            var attribute = memberInfo.GetCustomAttributes(inherit).FirstOrDefault(a => a.GetType().Equals(attributeType));
            if (attribute == null)
            {
                return null;
            }
            var properties = new Dictionary<string, object>();
            foreach (var propertyInfo in attribute.GetType().GetProperties(bindingAttr))
            {
                var propertyValue = propertyInfo.GetValue(attribute);
                if (propertyValue != null)
                {
                    properties.Add(propertyInfo.Name, propertyValue);
                }
            }
            return properties;
        }

        /// <summary>
        /// 获取特性的所有值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="memberInfo"></param>
        /// <param name="inherit">true:包含父类,false：不包含父类</param>
        /// <returns></returns>
        public static Dictionary<string, object> GetAttributeProperties<T>(this MemberInfo memberInfo, bool inherit = true) where T : Attribute
        {
            var attribute = memberInfo.GetCustomAttribute<T>(inherit);
            if (attribute == null)
            {
                return null;
            }
            var properties = new Dictionary<string, object>();
            foreach (var propertyInfo in attribute.GetType().GetProperties(bindingAttr))
            {
                var propertyValue = propertyInfo.GetValue(attribute);
                if (propertyValue != null)
                {
                    properties.Add(propertyInfo.Name, propertyValue);
                }
            }
            return properties;
        }

        /// <summary>
        /// 获取包含该特性的所有属性
        /// </summary>
        /// <param name="propertyInfos"></param>
        /// <param name="inherit">true:包含父类,false：不包含父类</param>
        /// <returns></returns>
        public static IEnumerable<PropertyInfo> GetPropertyInfosWithAttribute(IEnumerable<PropertyInfo> propertyInfos, Type attributeType, bool inherit = true)
        {
            return propertyInfos.Where(t => t.IsDefined(attributeType, inherit));
        }

        /// <summary>
        /// 获取包含该特性的所有方法
        /// </summary>
        /// <param name="fields"></param>
        /// <param name="inherit">true:包含父类,false：不包含父类</param>
        /// <returns></returns>
        public static IEnumerable<MemberInfo> GetMemberInfosWithAttribute(IEnumerable<MemberInfo> memberInfos, Type attributeType, bool inherit = true)
        {
            return memberInfos.Where(t => t.IsDefined(attributeType, inherit));
        }

        /// <summary>
        /// 获取包含该特性的所有字段
        /// </summary>
        /// <param name="fields"></param>
        /// <param name="inherit">true:包含父类,false：不包含父类</param>
        /// <returns></returns>
        public static IEnumerable<FieldInfo> GetFieldInfosWithAttribute(IEnumerable<FieldInfo> fields, Type attributeType, bool inherit = true)
        {
            return fields.Where(t => t.IsDefined(attributeType, inherit));
        }

        /// <summary>
        /// 获取特性
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="inherit">true:包含父类,false：不包含父类</param>
        /// <returns></returns>
        public static T GetAttribute<T>(this Type type, bool inherit = true) where T : Attribute
        {
            return (T)type.GetCustomAttribute(typeof(T), inherit);
        }

        /// <summary>
        /// 判断是否有某个特性
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="memberInfo"></param>
        /// <param name="inherit">true:包含父类,false：不包含父类</param>
        /// <returns></returns>
        public static bool HasAttribute<T>(MemberInfo memberInfo, bool inherit = true) where T : Attribute
        {
            try
            {
                return Attribute.IsDefined(memberInfo, typeof(T), inherit);
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 判断是否有某个特性
        /// </summary>
        /// <param name="memberInfo"></param>
        /// <param name="attributeType"></param>
        /// <param name="inherit">true:包含父类,false：不包含父类</param>
        /// <returns></returns>
        public static bool HasAttribute(this MemberInfo memberInfo, Type attributeType, bool inherit = true)
        {
            try
            {
                return Attribute.IsDefined(memberInfo, attributeType, inherit);
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 获取特性属性值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="attributeName"></param>
        /// <param name="propertyName"></param>
        /// <param name="inherit">true:包含父类,false：不包含父类</param>
        /// <returns></returns>
        public static T GetAttributeValue<T>(this MemberInfo memberInfo, string attributeName, string propertyName, bool inherit = true)
        {
            var attribute = memberInfo.GetCustomAttributes(inherit).FirstOrDefault(a => a.GetType().Name == attributeName);
            if (attribute == null) return default;
            var property = attribute.GetType().GetProperty(propertyName, bindingAttr);
            if (property == null) return default;
            return (T)property.GetValue(attribute);
        }

        /// <summary>
        /// 获取特性属性值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="attributeName"></param>
        /// <param name="propertyName"></param>
        /// <param name="inherit">true:包含父类,false：不包含父类</param>
        /// <returns></returns>
        public static T GetAttributeValue<T>(this MemberInfo memberInfo, Type attributeType, string propertyName, bool inherit = true)
        {
            return memberInfo.GetAttributeValue<T>(attributeType.Name, propertyName, inherit);
        }

        /// <summary>
        /// 得到特性下描述
        /// </summary>
        /// <typeparam name="TAttribute">动态特性</typeparam>
        /// <param name="member"></param>
        /// <returns></returns>
        public static string ToDescription<TAttribute>(this MemberInfo member) where TAttribute : AttributeBase =>
            member.GetCustomAttribute<TAttribute>() is AttributeBase attributeBase ? attributeBase.Description() : member.Name;

        /// <summary>
        /// 获取实体类/枚举类的描述值
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string GetClassDescription(Type type)
        {
            DescriptionAttribute descriptionAttribute = type.GetCustomAttributes(typeof(DescriptionAttribute), false).FirstOrDefault<object>() as DescriptionAttribute;
            return descriptionAttribute == null ? type.ToString() : descriptionAttribute.Description;
        }

        /// <summary>
        /// 获取实体类所有字段属性描述值集合
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static Dictionary<string, string> GetFieldsDescription(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }
            BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance;
            Dictionary<string, string> dict = new Dictionary<string, string>();
            foreach (FieldInfo field in type.GetFields(bindingAttr))
            {
                dict.Add(field.Name, GetDescription(field));
            }
            foreach (PropertyInfo property in type.GetProperties(bindingAttr))
            {
                dict.Add(property.Name, GetDescription(property));
            }
            return dict;
        }

        /// <summary>
        /// 获取描述值
        /// </summary>
        /// <param name="memberInfo"></param>
        /// <returns></returns>
        private static string GetDescription(MemberInfo memberInfo)
        {
            DescriptionAttribute[] attributes = (DescriptionAttribute[])memberInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
            return attributes.Any() ? attributes[0].Description : string.Empty;
        }

        public static T GetAttribute<T>(MemberInfo info)
        {
            try
            {
                object[] customAttributes = info.GetCustomAttributes(typeof(T), true);
                if (customAttributes != null && customAttributes.Length > 0)
                {
                    return (T)((object)customAttributes[0]);
                }
            }
            catch
            {
            }
            return default(T);
        }

        public static T GetAttribute<T>(Type type)
        {
            object[] customAttributes = type.GetCustomAttributes(typeof(T), true);
            T result;
            if (customAttributes != null && customAttributes.Length > 0)
            {
                result = (T)((object)customAttributes[0]);
            }
            else
            {
                result = default(T);
            }
            return result;
        }

        public static T GetAttribute<T>(Assembly assembly)
        {
            object[] customAttributes = assembly.GetCustomAttributes(typeof(T), true);
            T result;
            if (customAttributes != null && customAttributes.Length > 0)
            {
                result = (T)((object)customAttributes[0]);
            }
            else
            {
                result = default(T);
            }
            return result;
        }

        public static T[] GetAttributes<T>(Type info) where T : Attribute
        {
            IEnumerable<T> customAttributes = (IEnumerable<T>)info.GetCustomAttributes(true);
            T[] result;
            if (customAttributes == null && customAttributes.Count<T>() == 0)
            {
                result = null;
            }
            else
            {
                result = customAttributes.ToArray<T>();
            }
            return result;
        }
    }
}